// <copyright file="AuthorisationPolicyManager.cs" company="SharpSTS">
// Copyright (c) 2007, 2008 All Right Reserved, http://sharpsts.com/
//
// This source is subject to the Microsoft Permissive License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// </copyright>
// <author>Barry Dorrans</author>
// <email>barryd@idunno.org</email>
// <date>2008-06-14</date>
// <summary>Provides access to the AuthorisationPolicyProvider.</summary>

namespace SharpSTS.Providers
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Configuration;
    using System.Globalization;
    using System.IdentityModel.Claims;
    using System.ServiceModel;
    using System.Web.Configuration;

    using Configuration;
    using Properties;

    /// <summary>
    /// The entry point to the provider model for filling claims.
    /// </summary>
    internal static class AuthorisationPolicyManager
    {   
        /// <summary>
        /// Provides a syncronisation object for thread safe provider loading.
        /// </summary>
        private static readonly object syncroot = new object();

        /// <summary>
        /// The selected <see cref="IAuthorisationPolicyProvider"/>.
        /// </summary>
        private static IAuthorisationPolicyProvider provider;

        /// <summary>
        /// Gets the <see cref="IAuthorisationPolicyProvider"/> provider.
        /// </summary>
        /// <value>The <see cref="IAuthorisationPolicyProvider"/> provider.</value>
        public static IAuthorisationPolicyProvider Provider
        {
            get
            {
                LoadProviders();
                return provider;
            }
        }

        /// <summary>
        /// Provides a list of claims for the specified claim types with specified rights assigned to each of the claims.
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="relyingParty">The <see cref="EndpointAddress"/> of the relying party that is requesting the claims.</param>
        /// <param name="claimTypes">A collection of claim types to provide the information for.</param>
        /// <param name="rights">The rights to assign to the fufilled claims.</param>
        /// <returns>A list of claims for the specified claim types with specified rights assigned to those claims.</returns>
        public static Collection<Claim> GetClaims(object userId, EndpointAddress relyingParty, ReadOnlyCollection<string> claimTypes, string rights)
        {
            return Provider.GetClaims(userId, relyingParty, claimTypes, rights);
        }

        /// <summary>
        /// Provides a list of claims for the specified claim types with the PossessProperty right assigned to those claims.
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="relyingParty">The <see cref="EndpointAddress"/> of the relying party that is requesting the claims.</param>
        /// <param name="claimTypes">A collection of claim types to provide the information for.</param>
        /// <returns>A list of claims for the specified claim types with the PossessProperty right assigned to those claims.</returns>
        public static Collection<Claim> GetClaims(object userId, EndpointAddress relyingParty, ReadOnlyCollection<string> claimTypes)
        {
            return GetClaims(userId, relyingParty, claimTypes, Rights.PossessProperty);
        }

        /// <summary>
        /// Provides a list of claims for the specified claim types with the PossessProperty right assigned to those claims.
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="claimTypes">A collection of claim types to provide the information for.</param>
        /// <returns>A list of claims for the specified claim types with the PossessProperty right assigned to those claims.</returns>
        public static Collection<Claim> GetClaims(object userId, ReadOnlyCollection<string> claimTypes)
        {
            return GetClaims(userId, null, claimTypes, Rights.PossessProperty);
        }

        /// <summary>
        /// Provides the information for the specified claim with a PossessProperty right.
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="claimType">The claim to provide the information for.</param>
        /// <returns>The information for the specified claim with a PossessProperty right.</returns>
        public static Claim GetClaim(object userId, string claimType)
        {
            return GetClaim(userId, null, claimType, Rights.PossessProperty);
        }

        /// <summary>
        /// Provides the information for the specified claim with a PossessProperty right
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="relyingParty">The <see cref="EndpointAddress"/> of the relying party that is requesting the claims.</param>
        /// <param name="claimType">The claim to provide the information for.</param>
        /// <returns>The information for the specified claim with a PossessProperty right.</returns>
        public static Claim GetClaim(object userId, EndpointAddress relyingParty, string claimType)
        {
            return GetClaim(userId, relyingParty, claimType, Rights.PossessProperty);
        }

        /// <summary>
        /// Provides the information for the specified claim.
        /// </summary>
        /// <param name="userId">The user identifier for the current request.</param>
        /// <param name="relyingParty">The <see cref="EndpointAddress"/> of the relying party that is requesting the claims.</param>
        /// <param name="claimType">The claim to provide the information for.</param>
        /// <param name="rights">The rights to provide for this claim.</param>
        /// <returns>The information for the specified claim.</returns>
        public static Claim GetClaim(object userId, EndpointAddress relyingParty, string claimType, string rights)
        {
            List<string> claimList = new List<string> 
                {
                    claimType
                };

            Collection<Claim> filledClaims = Provider.GetClaims(userId, relyingParty, claimList.AsReadOnly(), rights);
            if (filledClaims.Count == 0)
            {
                throw new ArgumentException(Resources.ErrorClaimWasNotFulfilled);
            }

            return filledClaims[0];
        }

        /// <summary>
        /// Returns the display description for a given claim URI.
        /// </summary>
        /// <param name="claimType">The claim to provide the display name for.</param>
        /// <returns>
        /// The display description for the specified claim.
        /// </returns>    
        public static string GetDisplayName(string claimType)
        {
            return Provider.GetDisplayName(claimType);
        }

        /// <summary>
        /// Validates the association between the authenicated user and the card id used during the request.
        /// </summary>
        /// <param name="userName">User name sent during authentication.</param>
        /// <param name="cardId">Card identity of the card used to send the request.</param>
        /// <param name="cardVersion">The version number of the card presented.</param>
        /// <returns>True if the username is authorised to use the card, otherwise false.</returns>
        /// <remarks>Override this method to provide your own authorisation process.</remarks>
        public static bool AuthorisedToUseCardId(string userName, string cardId, int cardVersion)
        {
            return Provider.AuthorisedToUseCardId(userName, cardId, cardVersion);
        }

        /// <summary>
        /// Validates the association between the PPID for a self-issued backed card
        /// and the card id used during the request.
        /// </summary>
        /// <param name="ppidClaim">User name sent during authentication.</param>
        /// <param name="issuerPublicKeyHash">A hash of the public key of the issuer for the PPID claim.</param>
        /// <param name="cardId">Card identity of the card used to send the request.</param>
        /// <param name="cardVersion">The version number of the card presented.</param>
        /// <returns>True if the username is authorised to use the card, otherwise false.</returns>
        /// <remarks>Override this method to provide your own authorisation process.</remarks>
        public static bool AuthorisedToUseCardId(Claim ppidClaim, string issuerPublicKeyHash, string cardId, int cardVersion)
        {
            return Provider.AuthorisedToUseCardId(ppidClaim, issuerPublicKeyHash, cardId, cardVersion);
        }

        /// <summary>
        /// Returns a card identifier for the supplied username, card type and card version number.
        /// </summary>
        /// <param name="userId">The user identifier to generation the card identifier for.</param>
        /// <param name="authenticationType">The type of the authentication used for the card.</param>
        /// <returns>An identifier for an information card.</returns>
        public static string GetCardIdForUserId(object userId, AuthenticationType authenticationType)
        {
            return Provider.GetCardIdForUserId(userId, authenticationType);
        }

        /// <summary>
        /// Returns a user identifier that the managed information card belongs to.
        /// </summary>
        /// <param name="cardId"> The card identifier.</param>
        /// <param name="authenticationType">The type of the authentication used for the card.</param>
        /// <returns>The user identifier the card belongs to.</returns>
        public static object GetUserIdFromCardId(string cardId, AuthenticationType authenticationType)
        {
            return Provider.GetUserIdFromCardId(cardId, authenticationType);
        }

        /// <summary>
        /// Authenticates a username and password
        /// </summary>
        /// <param name="userName">The username to authenticate.</param>
        /// <param name="password">The password to authenticate.</param>
        /// <returns>True if athenticated, otherwise false.</returns>
        public static bool AuthenticateUserNamePassword(string userName, string password)
        {
            return Provider.AuthenticateUserNamePassword(userName, password);
        }

        /// <summary>
        /// Decides if the information card used requires a refresh.
        /// </summary>
        /// <param name="cardId">The card identifier.</param>
        /// <param name="cardVersion">The card version.</param>
        /// <returns>True if a card refresh is required, otherwise false.</returns>
        public static bool IsCardRefreshRequired(string cardId, int cardVersion)
        {
            return Provider.IsCardRefreshRequired(cardId, cardVersion);
        }

        /// <summary>
        /// Gets the PPID.
        /// </summary>
        /// <param name="cardId">The card identifier for the current request</param>
        /// <param name="endpoint">The relaying party <see cref="EndpointAddress"/>.</param>
        /// <param name="selectorSuggestedPPID">The selector suggested PPID.</param>
        /// <returns>A PPID for the current card identifier and endpoint.</returns>
        /// <remarks>Implementation of this method is optional; PPID generation may be provided by a seperate <see cref="PPIDGeneratorProvider"/></remarks>
        public static string GetPPID(string cardId, EndpointAddress endpoint, string selectorSuggestedPPID)
        {
            return Provider.GetPPID(cardId, endpoint, selectorSuggestedPPID);
        }

        /// <summary>
        /// Loads the provider settings and initialisers the selected provider.
        /// </summary>
        private static void LoadProviders()
        {
            if (null != provider)
            {
                return;
            }

            lock (syncroot)
            {
                if (null != provider)
                {
                    return;
                }

                string providerSectionPath = string.Format(
                    CultureInfo.InvariantCulture,
                    @"{0}/{1}",
                    ProvidersSectionGroup.ConfigurationSectionGroupName,
                    AuthorisationPolicyProviderSection.ConfigurationSectionName);
                AuthorisationPolicyProviderSection section =
                    (AuthorisationPolicyProviderSection)
                    ConfigurationManager.GetSection(providerSectionPath);

                AuthorisationPolicyProviderCollection providers = new AuthorisationPolicyProviderCollection();
                ProvidersHelper.InstantiateProviders(section.Providers, providers, typeof(IAuthorisationPolicyProvider));
                provider = providers[section.DefaultProvider];
            }
        }
    }
}
